package com.ftwj.demo.global.shiro;

import java.util.concurrent.atomic.AtomicInteger;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ftwj.demo.permission.dao.UserDao;
import com.ftwj.demo.permission.entity.UserBean;

import cn.hutool.core.util.StrUtil;

/**
 *
 * <p> Title: RetryLimitHashedCredentialsMatcher </p>
 * <p> Description: 描述：自定义登录次数限制比较器 </p>
 * <p> Copyright: Copyright (c) 2020 </p>
 * <p> Company: 兰州飞天网景信息产业有限公司 </p>
 * 
 * @author WangSLi
 * @date 2020年5月29日 上午8:56:44
 * @version V1.0
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
	
	private static final String ERROR_MESSAGE="账户【 {} 】已锁定，请于5分钟后重试！";
	private static final String TIP_MESSAGE="还可以尝试【  {} 】次，账户即将锁定！";
	private static final Logger LOGGER = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);
	private Cache<String, AtomicInteger> passwordRetryCache;
	@Autowired
	private UserDao dao;
	
	public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
		passwordRetryCache = cacheManager.getCache("password:retry");
	}

	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		// 获取用户名
		String username = (String) token.getPrincipal();
		Wrapper<UserBean> queryWrapper=new QueryWrapper<UserBean>().eq("login_name", username).or().eq("phone", username);
		UserBean user = dao.selectOne(queryWrapper);
		if(ObjectUtils.isEmpty(user)){
            throw new UnknownAccountException();
        }
        if(user.getState()!=1){
            throw new DisabledAccountException();
        }
		// 获取用户登录次数
		AtomicInteger count = passwordRetryCache.get(user.getPhone());
		if (count == null) {
			// 如果用户没有登陆过,登陆次数加1 并放入缓存
			count = new AtomicInteger(1);
			passwordRetryCache.put(username, count);
		}
		int number = count.intValue();
		if (number > 5) {
			// 如果用户登陆失败次数大于5次 抛出锁定用户异常 并修改数据库字段(修改数据库永久锁定，缓存时间内锁定)
			// user.setState(-2);
			// dao.updateById(user);
			// 抛出用户锁定异常
			LOGGER.info(StrUtil.format(ERROR_MESSAGE, user.getShowName()));
			throw new LockedAccountException(StrUtil.format(ERROR_MESSAGE, user.getShowName()));
		}
		// 判断用户账号和密码是否正确
		boolean matches = super.doCredentialsMatch(token, info);
		if (matches) {
			passwordRetryCache.remove(username);
		}else {
			count.incrementAndGet();
			passwordRetryCache.put(username,new AtomicInteger(count.getAndAdd(1)));
			LOGGER.info(StrUtil.format(TIP_MESSAGE,(5-number)));
			throw new LockedAccountException((5-number)>0?StrUtil.format(TIP_MESSAGE,(5-number)):StrUtil.format(ERROR_MESSAGE, user.getShowName()));
		}
		return matches;
	}

	/**
	 * 	根据用户名 解锁用户
	 * 
	 * @param username
	 * @return
	 * 
	 */
	public void unlockAccount(String username) {
		Wrapper<UserBean> queryWrapper=new QueryWrapper<UserBean>().eq("login_name", username).or().eq("phone", username);
		UserBean user = dao.selectOne(queryWrapper);
		if (user != null) {
			// 修改数据库的状态字段为锁定
			// user.setState(1);
			// dao.updateById(user);
			passwordRetryCache.remove(username);
		}
	}
}